// g++ main-conf-demo.cpp -o conf-demo
#include <iostream>
#include <fstream>
#include <map>
#include <vector>
using namespace std;
typedef unsigned int uint;

using namespace std;

/*
Configuration loads data from a file and stores text values by key.  Multiple values can be stored at each key.
Each line of the file containing the delimiter character choosen will define a key, value pair.
Default values can be installed and from a separate file and will be accessed in order to look up missing keys.
*/
class Configuration{
public:
	Configuration() : defaults(0){ }
	
	virtual ~Configuration(){ 
		if(defaults){ delete defaults; }
	}
	
	virtual void Parse(const char *path, char delimiter = ':'){
		const int len = 400;
		char line[len];
		FILE *fp = fopen(path, "r");
		if(fp){
			while(fgets(line, len, fp)){
				line[len - 1] = 0;
				if(strlen(line) > 3 && line[0] != '#'){
					line[strlen(line) - 1] = 0; // get rid of the \n
					char *sep = strchr(line, delimiter);
					if(sep){
						*sep = 0;
						Add(line, sep + 1);
					}
				}
			}
		}	
		else{
			printf("%s configuration file not found\n", path);
		}
	}
	
	virtual void Add(const char *key, const char *value){
		string k(key);
		StorageMap::iterator it = data.find(k);
		if(it == data.end()){
			data[k] = vector< string >();
		}
		data[k].push_back(string(value));
	}
	
	virtual const char *Text(const char *key, uint index = 0){
		string k(key);
		return Text(k, index);
	}
	
	virtual const char *Text(string &key, uint index = 0){
		StorageMap::iterator it = data.find(key);
		if(it == data.end()){ 
			if(defaults){ return defaults->Text(key, index); }
			else{         throw "not found";                 }
		}
		return data[key][index].c_str();
	}
	
	virtual double Number(const char *key, uint index = 0){
		string k(key);
		return atof( Text(k, index) );
	}
	
	virtual double Number(string &key, uint index = 0){
		return atof( Text(key, index) );
	}	
	
	virtual uint Count(const char *key){
		string k(key);
		return Count(k);
	}
	
	virtual uint Count(string &key){
		StorageMap::iterator it = data.find(key);
		if(it == data.end()){ return 0; }
		return data[key].size();
	}
	
	virtual void AppendDefaults(const char *path){
		Configuration *last = this;
		while(last->defaults){
			last = last->defaults;
		}
		last->defaults = new Configuration();
		last->defaults->Parse(path);
	}
	
private:
	typedef map< string, vector< string > > StorageMap;
	StorageMap data;
	Configuration *defaults;
};

int main (int argc, char * const argv[]) {
    Configuration conf;
	if(argc == 1){
		puts("usage: ./conf-demo path_to_conf_file");
	}
	else{
		conf.Parse(argv[1]);
		printf("%s, %g, %s\n", conf.Text("a"), conf.Number("b"), conf.Text("test"));
	}
    return 0;
}
